var GlobalInputHandlers = {};

var BaseInputHandler = Base.extend({
    initialize: function initialize(eventNames) {
        this.enabled = false;
        this.subscribers = {};
        if (eventNames != null)
            this.eventNames = eventNames;
        else
            this.eventNames = [];
    },
    isAvailable: function isAvailable() {
        return true;
    },
    subscribe: function subscribe(name, handler) {
        this.subscribers[name] = handler;
    },
    unsubscribe: function subscribe(name) {
        this.subscribers[name] = null;
    },
    setup: function setup() {
    },
    teardown: function teardown() {
        if (this.enabled) {
            this.enabled = false;
            for (var i = 0; i < this.eventNames.length; i++) {
                $(window).off(this.eventNames[i]);
            }
        }
    }
});

var KeyboardInput = BaseInputHandler.extend({
    initialize: function initialize() {
        BaseInputHandler.initialize.call(this, ["keydown.keyboard-input", "keyup.keyboard-input"]);
    },
    sendKeyDown: function sendKeyDown(context) {
        for (subId in this.subscribers) {
            this.subscribers[subId].fireEvent("keydown", context);
        }
    },
    sendKeyUp: function sendKeyUp(context) {
        for (subId in this.subscribers) {
            this.subscribers[subId].fireEvent("keyup", context);
        }
    },
    setup: function setup() {
        if (!this.enabled) {
            this.enabled = true;
            var self = this;
            $(window).on('keydown.keyboard-input', function (event) {
                var context = { prevent: false, which: event.which };
                self.sendKeyDown(context);
                if (context.prevent) event.preventDefault();
            });
            $(window).on('keyup.keyboard-input', function (event) {
                var context = { prevent: false, which: event.which };
                self.sendKeyUp(context);
                if (context.prevent) event.preventDefault();
            });
        }
    }
});

var TapInput = BaseInputHandler.extend({
    initialize: function initialize() {
        BaseInputHandler.initialize.call(this, ["vclick.touch-input", "tap.touch-input", "taphold.touch-input", "swipe.touch-input"]);
    },
    sendVClick: function sendVClick(context) {
        for (subId in this.subscribers) {
            this.subscribers[subId].fireEvent("vclick", context);
        }
    },
    sendTap: function sendTap(context) {
        for (subId in this.subscribers) {
            this.subscribers[subId].fireEvent("tap", context);
        }
    },
    sendTapHold: function sendTapHold(context) {
        for (subId in this.subscribers) {
            this.subscribers[subId].fireEvent("taphold", context);
        }
    },
    sendSwipe: function sendSwipe(context) {
        for (subId in this.subscribers) {
            this.subscribers[subId].fireEvent("swipe", context);
        }
    },
    setup: function setup() {
        if (!this.enabled) {
            this.enabled = true;
            var self = this;
            $(window).on('vclick.touch-input', function (event) {
                var context = { prevent: false, clientX: event.clientX, clientY: event.clientY };
                self.sendVClick(context);
                if (context.prevent) event.preventDefault();
            });
            $(window).on('tap.touch-input', function (event) {
                var context = { prevent: false, event: event };
                self.sendVClick(context);
                if (context.prevent) event.preventDefault();
            });
            $(window).on('taphold.touch-input', function (event) {
                var context = { prevent: false, event: event };
                self.sendVClick(context);
                if (context.prevent) event.preventDefault();
            });
            $(window).on('swipe.touch-input', function (event) {
                var context = { prevent: false, event: event };
                self.sendVClick(context);
                if (context.prevent) event.preventDefault();
            });
        }
    }
});

var GpsInput = BaseInputHandler.extend({
    initialize: function initialize() {
        BaseInputHandler.initialize.call(this, []);
        this.readingCount = 0;
        this.lastLatitude = 0;
        this.lastLongitude = 0;
        this.errorCount = 0;
        this.timerHandle = null;
        this.lastError = null;
        this.lastWasError = false;
        this.timerInterval = 1000;
    },
    isAvailable: function isAvailable() {
        return ('geolocation' in navigator);
    },
    sendGpsCoordinates: function sendGpsCoordinates(context) {
        if (this.lastLatitude != context.latitude || this.lastLongitude != context.longitude) {
            this.lastLatitude = context.latitude;
            this.lastLongitude = context.longitude;
            this.readingCount++;
            this.lastWasError = false;
            for (subId in this.subscribers) {
                this.subscribers[subId].fireEvent("gps", context);
            }
        }
    },
    setup: function setup() {
        if (!this.enabled) {
            this.enabled = true;
            var self = this;

            self.timerHandle = setInterval(function () {
                navigator.geolocation.getCurrentPosition(
                    function (position) { // on success
                        if (position != null && ("coords" in position)
                            && !isNaN(position.coords.latitude) && !isNaN(position.coords.longitude)) {

                            var context = { prevent: false, latitude: position.coords.latitude, longitude: position.coords.longitude };
                            self.sendGpsCoordinates(context);
                        }
                    }, 
                    function (error) { // on error
                        self.errorCount++;
                        self.lastError = error;
                        self.lastWasError = true;
                    },
                    { maximumAge: 60000, timeout: 5000, enableHighAccuracy: false }); // options
            }, self.timerInterval);
        }
    },
    teardown: function teardown() {
        if (this.enabled) {
            this.enabled = false;
            clearTimeout(this.timerHandle);
        }
    }
});

var TestGpsInput = BaseInputHandler.extend({
    initialize: function initialize() {
        BaseInputHandler.initialize.call(this, []);
        this.readingCount = 0;
        this.errorCount = 0;
        this.lastError = null;
        this.lastWasError = false;
        this.geo = null;
        this.timerHandle = null;
        this.timerInterval = 1000;
        this.sampleFeed = [
            { lat: 39.955813, lon: -86.011362 },
            { lat: 39.960744, lon: -86.01546 },
            { lat: 39.960512, lon: -86.02856 },
            { lat: 39.956142, lon: -86.039257 },
            { lat: 39.970549, lon: -86.034107 },
            { lat: 39.971273, lon: -86.004324 }];
        this.sampleFeedIndex = 0;
    },
    sendGpsCoordinates: function sendGpsCoordinates(context) {
        this.readingCount++;
        for (subId in this.subscribers) {
            this.subscribers[subId].fireEvent("gps", context);
        }
    },
    setup: function setup() {
        if (!this.enabled) {
            this.enabled = true;
            var self = this;

            self.timerHandle = setInterval(function () {
                var location = self.sampleFeed[self.sampleFeedIndex];
                self.sampleFeedIndex++;
                if (self.sampleFeedIndex >= self.sampleFeed.length)
                    self.sampleFeedIndex = 0;

                var context = { prevent: false, latitude: location.lat, longitude: location.lon };
                self.sendGpsCoordinates(context);
            }, self.timerInterval);
        }
    },
    teardown: function teardown() {
        if (this.enabled) {
            this.enabled = false;
            clearTimeout(this.timerHandle);
        }
    }
});

var OrientationInput = BaseInputHandler.extend({
    initialize: function initialize() {
        BaseInputHandler.initialize.call(this, []);
        this.lastAlpha = 0;
        this.lastBeta = 0;
        this.lastGamma = 0;
        this.lastAbsolute = 0;
        this.eventName = "deviceorientation";

        var self = this;
        this.handler = function (event) {
            var context = {
                prevent: false,
                alpha: event.alpha,
                beta: event.beta,
                gamma: event.gamma,
                absolute: event.absolute
            };

            self.sendOrientation(context);
        };
    },
    isAvailable: function isAvailable() {
        return (window.DeviceOrientationEvent);
    },
    sendOrientation: function sendOrientation(context) {
        if (this.lastAbsolute != context.absolute || this.lastAlpha != context.alpha ||
			this.lastBeta != context.beta || this.lastGamma != context.gamma) {

            if (null == context.alpha && null == context.beta &&
                null == context.gamma && null == context.absolute) {
                // empty readings shouldn't come back to the application
            }
            else {
                this.lastAlpha = context.alpha;
                this.lastBeta = context.beta;
                this.lastGamma = context.gamma;
                this.lastAbsolute = context.absolute;

                this.readingCount++;
                this.lastWasError = false;
                for (subId in this.subscribers) {
                    this.subscribers[subId].fireEvent("orientation", context);
                }
            }
        }
    },
    setup: function setup() {
        if (!this.enabled) {
            this.enabled = true;
            var self = this;
            window.addEventListener(this.eventName, this.handler, false);
        }
    },
    teardown: function teardown() {
        if (this.enabled) {
            this.enabled = false;
            clearTimeout(this.timerHandle);
            window.removeEventListener(this.eventName, this.handler, false);
        }
    }
});

var TestObjectLocationInput = BaseInputHandler.extend({
    initialize: function initialize() {
        BaseInputHandler.initialize.call(this, []);
        this.readingCount = 0;
        this.errorCount = 0;
        this.lastError = null;
        this.lastWasError = false;
        this.geo = null;
        this.timerHandle = null;
        this.timerInterval = 1000;
        this.sampleFeed = [
            { objId: 666, lat: 39.955813, lon: -86.011362 },
            { objId: 666, lat: 39.960744, lon: -86.01546 },
            { objId: 666, lat: 39.960512, lon: -86.02856 },
            { objId: 666, lat: 39.956142, lon: -86.039257 },
            { objId: 666, lat: 39.970549, lon: -86.034107 },
            { objId: 666, lat: 39.971273, lon: -86.004324 }];
        this.sampleFeedIndex = 0;
    },
    sendObjectCoordinates: function sendObjectCoordinates(context) {
        this.readingCount++;
        for (subId in this.subscribers) {
            this.subscribers[subId].fireEvent("objectlocation", context);
        }
    },
    setup: function setup() {
        if (!this.enabled) {
            this.enabled = true;
            var self = this;

            self.timerHandle = setInterval(function () {
                var location = self.sampleFeed[self.sampleFeedIndex];
                self.sampleFeedIndex++;
                if (self.sampleFeedIndex >= self.sampleFeed.length)
                    self.sampleFeedIndex = 0;

                var context = { prevent: false, oid: location.objId, latitude: location.lat, longitude: location.lon };
                self.sendObjectCoordinates(context);
            }, self.timerInterval);
        }
    },
    teardown: function teardown() {
        if (this.enabled) {
            this.enabled = false;
            clearTimeout(this.timerHandle);
        }
    }
});

//GlobalInputHandlers.Gps = GpsInput.newInstance();
GlobalInputHandlers.Gps = GpsInput.newInstance();
GlobalInputHandlers.Orientation = OrientationInput.newInstance();
GlobalInputHandlers.Keyboard = KeyboardInput.newInstance();
GlobalInputHandlers.Tap = TapInput.newInstance();
GlobalInputHandlers.ObjectLocation = TestObjectLocationInput.newInstance();